/*
* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "plugin_manager/include/plugin_manager.h"

#include "plugin_manager/include/plugin.h"
#include "protocol/retcode_inner/infer_retcode_inner.h"
#include "utils/infer_macros.h"
#include "utils/log/infer_log.h"

namespace OHOS {
namespace AI_STD {
int PluginManager::GetPlugin(const AlgorithmInfo &algoInfo, std::shared_ptr<Plugin> &plugin)
{
    HILOGI("[PluginManager]Get plugin for server, frwk=%d, aid=%s, version=%lld.", algoInfo.frameworkType, algoInfo.algorithmName.c_str(), algoInfo.algorithmVersion);
    PluginKey pluginKey(algoInfo.algorithmName, algoInfo.algorithmVersion, algoInfo.frameworkType);
    plugin = FindPlugin(pluginKey);
    if (plugin) {
        return RETCODE_SUCCESS;
    }

    std::lock_guard<std::mutex> lock(mutex4Interface_);
    plugin = FindPlugin(pluginKey);
    if (plugin) {
        return RETCODE_SUCCESS;
    }

    return LoadPlugin(algoInfo, plugin);
}

std::shared_ptr<Plugin> PluginManager::FindPlugin(const PluginKey &pluginKey)
{
    std::lock_guard<std::mutex> lock(mutex_);

    auto iter = pluginMap_.find(pluginKey);
    CHK_RET(pluginMap_.end() == iter, nullptr);

    return iter->second;
}

void PluginManager::AddPlugin(const PluginKey &pluginKey, const std::shared_ptr<Plugin> &pluginValue)
{
    std::lock_guard<std::mutex> lock(mutex_);
    pluginMap_[pluginKey] = pluginValue;
}

void PluginManager::DelPlugin(const PluginKey &pluginKey)
{
    std::lock_guard<std::mutex> lock(mutex_);
    pluginMap_.erase(pluginKey);
}

void PluginManager::DelPluginByAID(const std::string &aid)
{
    std::lock_guard<std::mutex> lock(mutex_);
    PluginMap::iterator iter = pluginMap_.begin();
    while (iter != pluginMap_.end()) {
        if (aid == iter->first.aid) {
            HILOGI("[PluginManager]Try to delete plugin. aid=%s, version=%lld.", iter->first.aid.c_str(),
                iter->first.version);
            pluginMap_.erase(iter);
        }
        ++iter;
    }
}

int PluginManager::LoadPlugin(const AlgorithmInfo &algoInfo, std::shared_ptr<Plugin> &plugin)
{
    auto pluginPtr = std::make_shared<Plugin>(Plugin(algoInfo.algorithmName, algoInfo.algorithmVersion, algoInfo.frameworkType));
    if (pluginPtr == nullptr) {
        HILOGE("[PluginManager]The plugin is null.");
        return RETCODE_OUT_OF_MEMORY;
    }
    int ret = pluginPtr->LoadPluginAlgorithm(algoInfo);
    if (ret != RETCODE_SUCCESS) {
        HILOGE("[PluginManager]Failed to load plugin(aid=%s, version=%lld, frwk=%d): ret=%d.", 
              algoInfo.algorithmName.c_str(), algoInfo.algorithmVersion, algoInfo.frameworkType, ret);
        return ret;
    }
    PluginKey pluginKey(algoInfo.algorithmName, algoInfo.algorithmVersion, algoInfo.frameworkType);
    AddPlugin(pluginKey, pluginPtr);
    plugin = pluginPtr;
    HILOGI("[PluginManager]End to load plugin: aid=%s, version=%lld, frwk=%d.", algoInfo.algorithmName.c_str(), algoInfo.algorithmVersion, algoInfo.frameworkType);
    return RETCODE_SUCCESS;
}

void PluginManager::UnloadPlugin(const std::string &aid, long long version, int frwk)
{
    PluginKey pluginKey(aid, version, frwk);
    std::shared_ptr<Plugin> plugin = FindPlugin(pluginKey);
    if (plugin) {
        DelPlugin(pluginKey);
        HILOGI("[PluginManager]End to unload plugin, frwk=%d, aid=%s, version=%lld, successfully.", 
            pluginKey.frwk, pluginKey.aid.c_str(), pluginKey.version);
        return;
    }
    HILOGI("[PluginManager]End to unload plugin, aid=%s, version=%lld, plugin is not exists, no need to unload.",
        pluginKey.aid.c_str(), pluginKey.version);
}

std::mutex PluginManager::instanceLock_;
PluginManager *PluginManager::instance_ = nullptr;

PluginManager *PluginManager::GetInstance()
{
    CHK_RET(instance_ != nullptr, instance_);

    std::lock_guard<std::mutex> lock(instanceLock_);
    CHK_RET(instance_ != nullptr, instance_);

    AI_NEW_INSTANCE(instance_, PluginManager);
    return instance_;
}

void PluginManager::Destroy()
{
    HILOGI("[PluginManager]Begin to Destroy plugin");
    pluginMap_.clear();
    AI_DELETE_INSTANCE(instance_);
}

PluginManager::PluginManager() = default;

PluginManager::~PluginManager() = default;

} // namespace AI_STD
} // namespace OHOS
